from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, timezone
import config  # 导入独立配置文件
import json
from dateutil import parser as date_parser

app = Flask(__name__)

# 使用独立配置文件中的数据库连接参数
app.config['SQLALCHEMY_DATABASE_URI'] = config.SQLALCHEMY_DATABASE_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = config.SQLALCHEMY_TRACK_MODIFICATIONS

db = SQLAlchemy(app)

class AlertEvent(db.Model):
    __tablename__ = 'alert_events'
    id = db.Column(db.Integer, primary_key=True)
    receiver = db.Column(db.String(64))      # 接收器名称
    status = db.Column(db.String(16))        # 告警状态（firing/resolved）
    fingerprint = db.Column(db.String(64))   # 告警唯一指纹
    group_alertname = db.Column(db.String(128))   # 分组标签：alertname
    group_instance = db.Column(db.String(128))    # 分组标签：instance
    description = db.Column(db.Text)         # 告警详情描述
    starts_at = db.Column(db.DateTime)       # 告警开始时间
    ends_at = db.Column(db.DateTime, nullable=True) # 告警结束时间
    recovered_at = db.Column(db.DateTime, nullable=True) # 恢复时间
    duration_minutes = db.Column(db.Integer, nullable=True) # 故障持续时长（分钟）
    generator_url = db.Column(db.Text)       # 生成告警的PromQL链接
    group_key = db.Column(db.Text)           # 分组键
    external_url = db.Column(db.Text)        # Alertmanager外部访问地址
    created_at = db.Column(db.DateTime, default=datetime.utcnow) # 记录插入时间

# 启动时自动建表
with app.app_context():
    db.create_all()

@app.route('/webhook', methods=['POST'])
def webhook():
    """
    接收Alertmanager推送的告警/恢复信息，解析后存入数据库。
    若为firing则插入或更新，若为resolved则更新恢复时间和持续时长。
    """
    data = request.json
    print("[webhook] 收到原始数据:")
    print(json.dumps(data, indent=2, ensure_ascii=False))
    group_labels = data.get('groupLabels', {})
    group_alertname = group_labels.get('alertname')
    group_instance = group_labels.get('instance')
    for alert in data.get('alerts', []):
        status = alert.get('status')
        fingerprint = alert.get('fingerprint')
        description = alert.get('annotations', {}).get('description')
        print(f"[webhook] annotations.description: {repr(description)}")
        generator_url = alert.get('generatorURL')
        group_key = data.get('groupKey')
        external_url = data.get('externalURL')
        receiver = data.get('receiver')
        # 处理时间格式
        def parse_time(timestr):
            if not timestr or timestr.startswith('0001'):
                return None
            try:
                return date_parser.parse(timestr)
            except Exception:
                return None
        starts_at = parse_time(alert.get('startsAt'))
        ends_at = parse_time(alert.get('endsAt'))
        print(f"[webhook] 处理alert: status={status}, fingerprint={fingerprint}, starts_at={starts_at}, ends_at={ends_at}")
        if not starts_at:
            print(f"[webhook] startsAt 为空或格式错误，跳过该alert: fingerprint={fingerprint}")
            continue
        if status == 'firing':
            exists = AlertEvent.query.filter_by(fingerprint=fingerprint, starts_at=starts_at).first()
            if not exists:
                event = AlertEvent(
                    receiver=receiver,
                    status=status,
                    fingerprint=fingerprint,
                    group_alertname=group_alertname,
                    group_instance=group_instance,
                    description=description,
                    starts_at=starts_at,
                    ends_at=ends_at,
                    generator_url=generator_url,
                    group_key=group_key,
                    external_url=external_url
                )
                print(f"[webhook] 即将入库的description: {repr(description)}")
                db.session.add(event)
                print(f"[webhook] 新增firing流水: fingerprint={fingerprint}, starts_at={starts_at}")
            else:
                print(f"[webhook] 已存在firing流水: fingerprint={fingerprint}, starts_at={starts_at}，不重复插入")
        elif status == 'resolved':
            event = AlertEvent.query.filter_by(fingerprint=fingerprint, starts_at=starts_at).first()
            recovered_at = ends_at or datetime.utcnow()
            if event and not event.ends_at:
                event.status = 'resolved'
                event.recovered_at = recovered_at
                event.ends_at = ends_at
                if event.starts_at and recovered_at:
                    s = event.starts_at
                    r = recovered_at
                    if not s.tzinfo:
                        s = s.replace(tzinfo=timezone.utc)
                    if not r.tzinfo:
                        r = r.replace(tzinfo=timezone.utc)
                    event.duration_minutes = round((r - s).total_seconds() / 60)
                print(f"[webhook] 更新为已恢复: fingerprint={fingerprint}, starts_at={starts_at}, ends_at={ends_at}, 持续分钟数={event.duration_minutes}")
            else:
                print(f"[webhook] 未找到对应firing流水或已恢复，resolved跳过: fingerprint={fingerprint}, starts_at={starts_at}")
        else:
            print(f"[webhook] 未知status类型，跳过: {status}")
        db.session.commit()
        print("[webhook] 数据库已提交")
    return "OK", 200

@app.route('/alerts', methods=['GET'])
def get_alerts():
    """
    查询历史告警信息，支持按状态、fingerprint等过滤，limit参数控制返回条数，默认100，limit=0返回所有。
    新增only_firing参数，1时只返回还在告警中的信息。
    新增时间过滤：days参数（如1d、7d）或start_time、end_time参数（格式：YYYYMMDDHHMMSS、YYYYMMDDHHMM 或 YYYYMMDD）
    """
    status = request.args.get('status')
    fingerprint = request.args.get('fingerprint')
    limit = request.args.get('limit', default=100, type=int)
    only_firing = request.args.get('only_firing', default=0, type=int)
    
    # 时间过滤参数
    days = request.args.get('days')  # 如：1d、7d、30d
    start_time = request.args.get('start_time')  # 如：20251001 00:00:00
    end_time = request.args.get('end_time')  # 如：20251010 00:00:00
    
    print(f"[alerts] 接收到的参数：days={days}, start_time={start_time}, end_time={end_time}")
    print(f"[alerts] 所有请求参数：{dict(request.args)}")
    
    query = AlertEvent.query
    
    # 状态过滤
    if only_firing == 1:
        query = query.filter_by(status='firing')
    elif status:
        query = query.filter_by(status=status)
    
    # fingerprint过滤
    if fingerprint:
        query = query.filter_by(fingerprint=fingerprint)
    
    # 时间过滤
    if days:
        # 解析天数参数，如1d、7d、30d
        try:
            days_num = int(days.replace('d', ''))
            from datetime import timedelta
            cutoff_time = datetime.utcnow() - timedelta(days=days_num)
            # 过滤掉starts_at为NULL的记录，只查询有时间的记录
            query = query.filter(AlertEvent.starts_at.isnot(None)).filter(AlertEvent.starts_at >= cutoff_time)
            print(f"[alerts] 时间过滤：最近{days_num}天，截止时间：{cutoff_time}")
        except (ValueError, AttributeError):
            print(f"[alerts] 无效的days参数：{days}")
    
    if start_time or end_time:
        # 解析标准时间格式：YYYYMMDDHHMMSS 或 YYYYMMDD
        try:
            if start_time:
                # 支持多种格式：20251001000000 或 20251001_000000 或 202510010000
                start_time_clean = start_time.replace('_', '').replace(' ', '').replace('-', '').replace(':', '').replace('"', '').replace("'", '')
                print(f"[alerts] 清理后的开始时间：{start_time_clean}, 长度：{len(start_time_clean)}")
                if len(start_time_clean) == 14:  # YYYYMMDDHHMMSS
                    start_dt = datetime.strptime(start_time_clean, '%Y%m%d%H%M%S')
                elif len(start_time_clean) == 12:  # YYYYMMDDHHMM，默认为00秒
                    start_dt = datetime.strptime(start_time_clean, '%Y%m%d%H%M')
                elif len(start_time_clean) == 8:  # YYYYMMDD，默认为00:00:00
                    start_dt = datetime.strptime(start_time_clean, '%Y%m%d')
                else:
                    raise ValueError(f"时间格式长度错误：{start_time}，清理后：{start_time_clean}，长度：{len(start_time_clean)}")
                query = query.filter(AlertEvent.starts_at.isnot(None)).filter(AlertEvent.starts_at >= start_dt)
                print(f"[alerts] 开始时间过滤：{start_dt}")
            
            if end_time:
                # 支持多种格式：20251010000000 或 20251010_000000 或 202510100000
                end_time_clean = end_time.replace('_', '').replace(' ', '').replace('-', '').replace(':', '').replace('"', '').replace("'", '')
                print(f"[alerts] 清理后的结束时间：{end_time_clean}, 长度：{len(end_time_clean)}")
                if len(end_time_clean) == 14:  # YYYYMMDDHHMMSS
                    end_dt = datetime.strptime(end_time_clean, '%Y%m%d%H%M%S')
                elif len(end_time_clean) == 12:  # YYYYMMDDHHMM，默认为59秒
                    end_dt = datetime.strptime(end_time_clean, '%Y%m%d%H%M').replace(second=59)
                elif len(end_time_clean) == 8:  # YYYYMMDD，默认为23:59:59
                    end_dt = datetime.strptime(end_time_clean, '%Y%m%d').replace(hour=23, minute=59, second=59)
                else:
                    raise ValueError(f"时间格式长度错误：{end_time}，清理后：{end_time_clean}，长度：{len(end_time_clean)}")
                query = query.filter(AlertEvent.starts_at.isnot(None)).filter(AlertEvent.starts_at <= end_dt)
                print(f"[alerts] 结束时间过滤：{end_dt}")
        except ValueError as e:
            print(f"[alerts] 时间格式错误：{e}")
            return jsonify({"error": f"时间格式错误：{str(e)}"}), 400
    
    query = query.order_by(AlertEvent.starts_at.desc())
    
    if limit and limit > 0:
        results = query.limit(limit).all()
    else:
        results = query.all()
    
    # 返回主要字段，便于外部系统处理
    return jsonify([
        {
            "id": a.id,
            "status": a.status,
            "fingerprint": a.fingerprint,
            "group_alertname": a.group_alertname,
            "group_instance": a.group_instance,
            "starts_at": a.starts_at.isoformat() if a.starts_at else None,
            "ends_at": a.ends_at.isoformat() if a.ends_at else None,
            "recovered_at": a.recovered_at.isoformat() if a.recovered_at else None,
            "duration_minutes": a.duration_minutes,
            "description": a.description
        } for a in results
    ])

if __name__ == '__main__':
    # 启动Flask服务，监听0.0.0.0:5000
    app.run(host='0.0.0.0', port=5001) 
    #app.run(host='0.0.0.0', port=5000) 